﻿using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace FootSteps.Domain.Loggers.Screen
{
    public class PrintScreenLogger : DG.Common.Patterns.MVVM.ViewModelBase
    {
        public int? TargetWidthResize
        {
            get { return _targetWidthResize; }
            set
            {
                if (value == _targetWidthResize) return;
                _targetWidthResize = value;
                OnPropertyChanged("TargetWidthResize");
            }
        }

        public bool Started
        {
            get { return _started; }
            private set
            {
                if (value.Equals(_started)) return;
                _started = value;
                OnPropertyChanged("Started");
            }
        }

        public int SleepMillisenconds
        {
            get { return _sleepMillisenconds; }
            set
            {
                if (value == _sleepMillisenconds) return;
                _sleepMillisenconds = value;
                OnPropertyChanged("SleepMillisenconds");
            }
        }

        public byte[] LastScreenShotBytes
        {
            get { return _lastScreenShotBytes; }
            set
            {
                if (Equals(value, _lastScreenShotBytes)) return;
                _lastScreenShotBytes = value;
                OnPropertyChanged("LastScreenShotBytes");
            }
        }

        public bool SavePrints
        {
            get { return _savePrints; }
            set
            {
                if (value.Equals(_savePrints)) return;
                _savePrints = value;
                OnPropertyChanged("SavePrints");
            }
        }

        public delegate void PrintScreenTakenHandler(PrintScreenArgs e);
        public event PrintScreenTakenHandler OnPrintScreenTakenChange;

        private Task _task;
        private CancellationTokenSource _cancellationToken;

        public PrintScreenLogger()
        {
            SleepMillisenconds = 2000;
        }

        public void Start()
        {
            if (Started) return;
            Started = true;
            var taskCancellationToken = new CancellationTokenSource();
            _cancellationToken = taskCancellationToken;
            _task = Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    if (taskCancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    DoWork();
                    Thread.Sleep(SleepMillisenconds);
                }
            }, taskCancellationToken.Token);
            Console.WriteLine(GetType().Name + " Started");
        }
        public void Stop()
        {
            if (!Started) return;
            Started = false;
            
            _cancellationToken.Cancel(false);
            Console.WriteLine(GetType().Name + " Started");
        }

        private DateTime _lastFullPrint = DateTime.MinValue;
        private bool _started;
        private int _sleepMillisenconds;
        private byte[] _lastScreenShotBytes;
        private bool _savePrints;
        private int? _targetWidthResize;

        private Bitmap GetThumbPrint()
        {
            var screen = System.Windows.Forms.Screen.PrimaryScreen;
            var newWidth = TargetWidthResize ?? screen.Bounds.Width;
            var newHeight = (int) (screen.Bounds.Height 
                * ((double)newWidth /screen.Bounds.Width));

            using (var print = new Bitmap(screen.Bounds.Width, screen.Bounds.Height))
            using (var graphics = Graphics.FromImage(print))
            {
                graphics.CopyFromScreen(0, 0, 0, 0, print.Size);

                return (Bitmap) print.GetThumbnailImage(newWidth,newHeight, null,IntPtr.Zero);
            }
        }

        private void DoWork()
        {
            var now = DateTime.Now;
            using (var currentState = GetThumbPrint())
            using (var diffBitmap = new Bitmap(currentState))
            {
                var hasChange = false;
                const int maxNumberOfDifferencePrints = 50;
                
                if (LastScreenShotBytes != null && (now - _lastFullPrint).TotalMilliseconds < SleepMillisenconds * maxNumberOfDifferencePrints)
                {
                	using (var lastScreenShot = new Bitmap(new MemoryStream(LastScreenShotBytes))) {
                    	
	                    var lockedLastScreen = new LockBitmap(lastScreenShot);
	                    var lockedCurrentScrren = new LockBitmap(currentState);
	                    var lockedSaveBitMap = new LockBitmap(diffBitmap);
	                    lockedCurrentScrren.LockBits();
	                    lockedLastScreen.LockBits();
	                    lockedSaveBitMap.LockBits();
	
	                    var height = currentState.Height;
                        for (var x = 0; x < currentState.Width; x++)
                        for (var y = 0; y < height; y++)
                        {
                            var oldColor = lockedLastScreen.GetPixel(x, y);
                            var color = lockedCurrentScrren.GetPixel(x, y);

                            if (oldColor == color)
                            	lockedSaveBitMap.SetPixel(x, y, Color.Transparent);                            	
                            else
                                hasChange = true;
                        }
	
	                    lockedCurrentScrren.UnlockBits();
	                    lockedLastScreen.UnlockBits();
	                    lockedSaveBitMap.UnlockBits();
                    }                	
                }
                else
                {
                    _lastFullPrint = now;
                    hasChange = true;
                }
                LastScreenShotBytes = (byte[])new ImageConverter().ConvertTo(currentState, typeof(byte[]));


                if (hasChange)
                {
                    var isFullPrint = _lastFullPrint == now;
                    RaiseProcessChange(new PrintScreenArgs(now, isFullPrint,
                        (byte[]) new ImageConverter().ConvertTo(diffBitmap, typeof (byte[]))));
                }
                else
                {
                    Console.WriteLine("No change detected, Image not generated");
                }
            }

        }
        private void RaiseProcessChange(PrintScreenArgs args)
        {
            if (OnPrintScreenTakenChange != null)
                OnPrintScreenTakenChange(args);
        }
    }
}